/*
 * main.cpp
 *
 *  Created on: May 10, 2012
 *      Author: dblyth
 */

#include <fstream>
#include <iostream>
#include <pthread.h>
#include <sstream>
#include <stdlib.h>
#include <vector>
using namespace std;

#include <TH1D.h>
#include <TList.h>
#include <TMessage.h>
#include <TMonitor.h>
#include <TRandom.h>
#include <TServerSocket.h>
#include <TSocket.h>

#include "DAQRun.h"

TMonitor* mon;
TSocket* sock;
DAQRun* runObject;
bool serverContinue;

void getRegisters() {
	ifstream file("config_registers.tsv");
	if (file.fail()) {
		sock->Send("error");
		return;
	}
	int lineSize = 256;
	char* lineChar = new char[lineSize];

	while (!file.eof()) {
		file.getline(&lineChar[0], lineSize);
		string line = lineChar;

		string name;
		string address;
		string value;

		size_t pos = line.find('\t');
		if (pos != string::npos)
			name = line.substr(0, pos);
		else
			continue;

		line = line.substr(pos + 1);

		pos = line.find('\t');
		if (pos != string::npos)
			address = line.substr(0, pos);
		else
			continue;

		line = line.substr(pos + 1);

		pos = line.find('\t');
		if (pos != string::npos)
			value = line.substr(0, pos);
		else
			continue;

		line = line.substr(pos + 1);

		sock->Send((name + "\t" + address + "\t" + value + "\t" + line).c_str());
	}

	sock->Send("done");

	delete[] lineChar;
	file.close();
}

void getADCBaseAddr() {
	ifstream file("adc_base_addr");
	if (file.fail()) {
		sock->Send("error");
		return;
	}
	int lineSize = 256;
	char* lineChar = new char[lineSize];
	file.getline(&lineChar[0], lineSize);

	sock->Send(lineChar);

	delete[] lineChar;
	file.close();
}

void setADCBaseAddr(const char* address) {
	ofstream file("adc_base_addr");
	if (file.fail())
		return;

	file << address << EOF;

	file.close();
}

void getIsRunning() {
	string running = "false";

	if (runObject)
		if (runObject->isRunning())
			running = "true";

	sock->Send(running.c_str());
}

void getEventCount() {
	stringstream eventCount;
	eventCount << 0;

	if (runObject) {
		eventCount.str("");
		eventCount << runObject->getEventCount();
	}

	sock->Send(eventCount.str().c_str());
}

void getADCBufferLevel() {
	stringstream bufferLevel;
	bufferLevel << 100.0;

	if (runObject) {
		bufferLevel.str("");
		bufferLevel << runObject->getADCBufferLevel();
	}

	sock->Send(bufferLevel.str().c_str());
}

void getMemBufferLevel() {
	stringstream bufferLevel;
	bufferLevel << 100.0;

	if (runObject) {
		bufferLevel.str("");
		bufferLevel << runObject->getMemBufferLevel();
	}

	sock->Send(bufferLevel.str().c_str());
}

void getHistos() {
	if (runObject) {
		int histoCount = runObject->getHistoCount();
		for (int i = 0; i < histoCount; i++) {
			const char* name = runObject->getHistoName(i);
			sock->Send(name);
		}
		sock->Send("done");
	} else
		sock->Send("error");
}

void getRunName() {
	if (runObject) {
		const char* runName = runObject->getRunName();
		sock->Send(runName);
	} else
		sock->Send("");
}

void update(string name) {
	if (runObject) {
		TH1D* histo = runObject->getHistoCopy(name.c_str());
		if (histo) {
			TMessage mess(kMESS_OBJECT);
			mess.WriteObject(histo);
			sock->Send(mess);
			delete histo;
		} else
			sock->Send("error");
	} else
		sock->Send("error");
}

void startRun() {
	if (runObject) {
		if (runObject->isRunning()) {
			sock->Send("error");
			return;
		}

		delete runObject;
		runObject = NULL;
	}

	sock->Send("OK");

	runObject = new DAQRun();

	int bufferSize = 256;
	char* buffer = new char[bufferSize];
	sock->Recv(buffer, bufferSize);
	runObject->setNumPedSamples(atoi(buffer));

	sock->Recv(buffer, bufferSize);
	runObject->setDuration(atoi(buffer));

	ofstream ADCAddrFile("adc_base_addr");
	if (ADCAddrFile.fail()) {
		delete[] buffer;
		delete runObject;
		runObject = NULL;
		return;
	}

	sock->Recv(buffer, bufferSize);

	ADCAddrFile << buffer;

	ADCAddrFile.close();

	unsigned int ADCAddr;
	string ADCAddrString = buffer;
	size_t pos = ADCAddrString.find("0x");
	if (pos != string::npos)
		sscanf(ADCAddrString.substr(pos + 2).c_str(), "%x", &ADCAddr);
	else
		ADCAddr = atoi(buffer);

	runObject->setADCBaseAddress(ADCAddr);

	ofstream registerFile("config_registers.tsv");
	if (registerFile.fail()) {
		delete[] buffer;
		delete runObject;
		runObject = NULL;
		return;
	}

	sock->Recv(buffer, bufferSize);
	string msg = buffer;
	while (msg.compare("done") != 0) {
		registerFile << buffer << endl;

		size_t pos = msg.find('\t');
		string name = msg.substr(0, pos);
		msg = msg.substr(pos + 1);
		pos = msg.find('\t');
		string addressString = msg.substr(0, pos);
		msg = msg.substr(pos + 1);
		pos = msg.find('\t');
		string valueString = msg.substr(0, pos);
		msg = msg.substr(pos + 1);

		unsigned int address;
		unsigned int value;

		pos = addressString.find("0x");
		if (pos != string::npos)
			sscanf(addressString.substr(pos + 2).c_str(), "%x", &address);
		else
			address = atoi(addressString.c_str());

		pos = valueString.find("0x");
		if (pos != string::npos)
			sscanf(valueString.substr(pos + 2).c_str(), "%x", &value);
		else
			value = atoi(valueString.c_str());

		CVDataWidth dataWidth = cvD32;
		if (msg.compare("cvD16") == 0)
			dataWidth = cvD16;

		runObject->addConfigRegister(address, value, dataWidth);

		sock->Recv(buffer, bufferSize);
		msg = buffer;
	}

	ADCAddrFile.close();

	delete[] buffer;

	pthread_t runThread;
	pthread_create(&runThread, NULL, &DAQRun::run, (void*) runObject);
	pthread_detach(runThread);

	sock->Send("OK");
}

void stopRun() {
	if (runObject) {
		if (runObject->isRunning()) {
			runObject->setRunOff();
		}

		sock->Send("OK");
	} else
		sock->Send("error");
}

void termServer() {
	if (runObject) {
		if (runObject->isRunning()) {
			runObject->setRunOff();
		}
		delete runObject;
		runObject = NULL;
	}

	serverContinue = false;
	sock->Send("ok");
}

void commandHandler(char* msg) {
	string msgString = msg;
	delete[] msg;
	size_t pos = msgString.find(',');
	string command;
	string arg;
	if (pos != string::npos) {
		command = msgString.substr(0, pos);
		arg = msgString.substr(pos + 1);
	} else
		command = msgString;

	if (command.compare("exit") == 0) {
		mon->Remove(sock);
		sock->Send("ok");
		sock->Close();
	} else if (command.compare("update") == 0) {
		update(arg);
	} else if (command.compare("getHistos") == 0) {
		getHistos();
	} else if (command.compare("getRegisters") == 0) {
		getRegisters();
	} else if (command.compare("getADCBaseAddr") == 0) {
		getADCBaseAddr();
	} else if (command.compare("startRun") == 0) {
		startRun();
	} else if (command.compare("stopRun") == 0) {
		stopRun();
	} else if (command.compare("getIsRunning") == 0) {
		getIsRunning();
	} else if (command.compare("getEventCount") == 0) {
		getEventCount();
	} else if (command.compare("getADCBufferLevel") == 0) {
		getADCBufferLevel();
	} else if (command.compare("getMemBufferLevel") == 0) {
		getMemBufferLevel();
	} else if (command.compare("getRunName") == 0) {
		getRunName();
	} else if (command.compare("termServer") == 0) {
		termServer();
	} else
		sock->Send("error");
}

int main(int argc, char** argv) {
	runObject = NULL;

	if (argc != 2) {
		cout << "This program requires a single argument: the listening port number." << endl;
		return 0;
	}

	TServerSocket* ss = new TServerSocket(atoi(argv[1]), kTRUE);
	mon = new TMonitor();
	mon->Add(ss);

	TMessage::EnableSchemaEvolutionForAll(kTRUE);

	serverContinue = true;

	while (serverContinue) {
		sock = mon->Select(1000);

		if (sock != (TSocket*) -1) {
			if (sock->IsA() == TServerSocket::Class()) {
				TSocket* s = ((TServerSocket*) sock)->Accept();
				s->SetCompressionSettings(1);
				mon->Add(s);
			} else {
				char* messRecv = new char[1024];
				sock->Recv(messRecv, 1024);
				commandHandler(messRecv);
			}
		}

		TList* list = mon->GetListOfActives();
		for (int i = 0; i < list->GetEntries(); i++) {
			TSocket* tempSocket = (TSocket*) list->At(i);
			if (!tempSocket->IsValid())
				mon->Remove(tempSocket);
		}
	}

	TList* list = mon->GetListOfActives();
	for (int i = 0; i < list->GetSize(); i++)
		((TSocket*) list->At(i))->Close();

	delete mon;

	return 0;
}
